<?php
/**
 * Created by PhpStorm.
 * User: User
 * Date: 2017/11/23
 * Time: 10:04
 */

namespace Kinfy\Http;

use Kinfy\Config;

class Route
{

    protected static $routes = [];
    protected static $url_pre = [];
    protected static $patterns = [];


    //
    public static function pattern($holder, $regx)
    {
        self::$patterns[$holder] = $regx;
    }

    public static function match($req_methods, $url, $action)
    {
        foreach ($req_methods as $m) {
            self::{$m}($url, $action);
        }
    }

    //添加路由规则
    public static function __callStatic($name, $args)
    {
        list($url, $action) = $args;
        $pre = implode('', self::$url_pre);
        self::$routes[$name][$pre . $url] = $action;
    }

    public static function group($pre, $callback)
    {
        array_push(self::$url_pre, $pre);
        if (is_callable($callback)) {
            call_user_func($callback);
        }
        array_pop(self::$url_pre);
    }

    //正则匹配
    public static function getParams($url_pattern, $url)
    {
        if (strpos($url_pattern, '{') !== false) {
            //参数名
            preg_match_all('#{(.*?)}#', $url_pattern, $pnames);
            //参数值
            foreach ($pnames[1] as $p) {
                if (isset(self::$patterns[$p])) {
                    $regx = self::$patterns[$p];
                } else {
                    $regx = '.*?';
                }
                $url_pattern = str_replace(
                    '{' . $p . '}',
                    '(' . $regx . ')',
                    $url_pattern
                );
            }

            $matched = preg_match('#^' . $url_pattern . '$#', $url, $matchs);
            if ($matched) {
                array_shift($matchs);//删除第一个元素，后面都是捕获的参数结果
                return array_combine($pnames[1], $matchs);
            }
        }
        return false;
    }

    //路由转发
    public static function dispatch()
    {

        $matched = false;
        $m = strtolower($_SERVER['REQUEST_METHOD']);
        $p = strpos($_SERVER['REQUEST_URI'], '?');
        if ($p === false) {
            $u = $_SERVER['REQUEST_URI'];
        } else {
            $u = substr($_SERVER['REQUEST_URI'], 0, $p);
        }

        $u = rtrim($u,'/');
        if(!$u){
            $u = '/';
        }

        $routes = [];
        if (isset(self::$routes[$m])) {
            $routes = self::$routes[$m];
        }
        if (isset(self::$routes['any'])) {
            $routes = array_merge($routes, self::$routes['any']);
        }

        //是否匹配处理
        if (isset($routes[$u])) {
            $matched = true;
            $action = $routes[$u];
        } else {
            foreach ($routes as $url => $action) {
                $params = self::getParams($url, $u);
                if ($params) {
                    $matched = true;
                    break;
                }
            }
        }
        //print_r($params);
        //die;
        if ($matched) {
            if (is_callable($action)) {
                if (!empty($params)) {
                    $param_names = self::getMethodParams($action);
                    $param_values = [];
                    foreach ($param_names as $pn) {
                        if (isset($params[$pn])) {
                            $param_values [] = $params[$pn];
                        }
                    }
                    $action(...$param_values);
                } else {
                    $action();
                }
                return;
            }
            list($class, $method) = explode('@', $action);
            $prefix = Config::get('route.prefix');
            $suffix = Config::get('route.suffix');
            $class = $prefix . $class . $suffix;
            $ctl = new $class;
            if (!empty($params)) {
                $param_names = self::getMethodParams($method, $class);
                $param_values = [];
                foreach ($param_names as $pn) {
                    if (isset($params[$pn])) {
                        $param_values [] = $params[$pn];
                    }
                }
                $ctl->{$method}(...$param_values);
            } else {
                $ctl->{$method}();
            }

        } else {
            echo '404 NOT FOUND!';
        }
    }

    protected static function getMethodParams($method, $class = null)
    {
        $param_names = [];
        if ($class) {
            $reflector = new \ReflectionMethod($class, $method);
        } else {
            $reflector = new \ReflectionFunction($method);
        }
        foreach ($reflector->getParameters() as $p) {
            $param_names[] = $p->getName();
        }
        return $param_names;
    }
}